package org.uoss.applications.framework.dao.jpa.generic.impl;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.uoss.applications.framework.dao.jpa.generic.GenericDAO;
import org.uoss.applications.framework.dao.model.QueryModel;
import org.uoss.applications.framework.dao.model.ResultSetModel;
import org.uoss.applications.framework.dao.nativeSQL.impl.NativeSQLDAOImpl;
import org.uoss.applications.framework.dao.util.DAOCriteria;
import org.uoss.applications.framework.entity.GeneralObject;
import org.uoss.core.exception.UOSSException;
import org.uoss.core.util.ValidationUtil;

public class GenericDAOImpl<T extends GeneralObject> extends NativeSQLDAOImpl<T> implements GenericDAO<T> {
	@PersistenceContext(name="uoss-applications")
	protected EntityManager em;

	@SuppressWarnings("unchecked")
	public Collection<T> searchByHQL(String hql,Integer beginIndex,Integer fetchSize) throws UOSSException {
		Query query = this.em.createQuery(hql);
		query.setFirstResult(beginIndex);
		query.setMaxResults(fetchSize);
		return query.getResultList();
	}

	@SuppressWarnings("unchecked")
	public Collection<T> searchByHQL(String hql,List<Object> parameters) throws UOSSException {
		Query query = this.em.createQuery(hql);
		if(!ValidationUtil.isEmpty(parameters)){
			for(int i=0;i<parameters.size();i++){
				query.setParameter(i+1, parameters.get(i));
			}
		}
		return query.getResultList();
	}

	@SuppressWarnings("unchecked")
	public ResultSetModel<T> searchByHQL(String hql, QueryModel queryModel) throws UOSSException {
		Query query = this.em.createQuery(hql);
		Object[] parameters = null;
		if (queryModel != null && !ValidationUtil.isEmpty(queryModel.getQueryParameters())) {
			parameters = queryModel.getQueryParameters();
			for (int i = 0; i < parameters.length; i++) {
				query.setParameter(i+1, parameters[i]);
			}
		}
		query.setFirstResult(DAOCriteria.getFetchIndex(queryModel));
		query.setMaxResults(DAOCriteria.getMaxFetchSize(queryModel));
		List rst = query.getResultList();
		ResultSetModel<T> result = new ResultSetModel<T>();
		result.setItems(rst!=null?rst:new ArrayList<T>(0));
		result.setTotalCount(this.searchTotalCountByHQL(hql, parameters));
		return result;
	}

	@Override
	public int searchTotalCountByHQL(String hql) throws UOSSException {
		return this.searchTotalCountByHQL(hql, null);
	}

	@Override
	public int searchTotalCountByHQL(String hql, Object[] parameters) throws UOSSException {
		if(hql==null){
			return 0;
		}
		if(hql.indexOf("count(")==-1){
			hql = "select count(*) " + hql.substring(hql.lastIndexOf("from"));
		}
		Query query = this.em.createQuery(hql);
		if(!ValidationUtil.isEmpty(parameters)){
			for(int i=0;i<parameters.length;i++){
				query.setParameter(i+1, parameters[i]);
			}
		}
		Object result = query.getSingleResult();
		Long total = result==null?0:(Long)result; 
		return total.intValue();
	}

	@SuppressWarnings("unchecked")
	public Collection<T> searchBySQL(String sql,Class<T> entityClass, Integer beginIndex,Integer fetchSize) throws UOSSException {
		Query query = this.em.createNativeQuery(sql,entityClass);
		query.setFirstResult(beginIndex);
		query.setMaxResults(fetchSize);
		return query.getResultList(); 
	}

	@Override
	public int searchTotalCountBySQL(String sql) throws UOSSException {
		Query query = this.em.createNativeQuery(sql);
		Long result = (Long) query.getSingleResult();
		return result != null ? result.intValue() : 0;
	}

	@Override
	public Integer searchSequenceValueBySQL(String schema, String sequence)throws UOSSException {
		Query query = this.em.createNativeQuery(this.buildSequenceSQL(schema, sequence));
		BigDecimal sequenceValue = (BigDecimal)query.getSingleResult();
		return sequenceValue.intValue();
	}

	@Override
	public int searchTotalCount(Class<T> entityClass) throws UOSSException {
		return this.searchTotalCountByHQL("select count(*) from " + entityClass.getName());
	}


	public T findByPK(Class<T> entityClass, Serializable id) throws UOSSException {
		return this.em.find(entityClass, id);
	}

	@SuppressWarnings("unchecked")
	public Collection<T> load(Class<T> entityClass) throws UOSSException {
		Collection<T> result = null;
		try{
			Query query = this.em.createQuery("from " + entityClass.getName());
			result = query.getResultList();
		}
		catch(RuntimeException e){
			throw new UOSSException("Fail to load entity:" + entityClass.getName(),e);
		}
		return result;
	}

	public void persist(T entity) throws UOSSException {
		try{
			this.em.persist(entity);
			this.em.flush();
		}
		catch(RuntimeException e){
			throw new UOSSException("Fail to persist entity:" + entity.getClass().getName(),e);
		}
	}

	public void update(T entity) throws UOSSException {
		try{
			this.em.merge(entity);
		}
		catch(RuntimeException e){
			throw new UOSSException("Fail to update entity:" + entity.getClass().getName(),e);
		}
	}

	public void delete(T entity) throws UOSSException {
		try {
			this.em.remove(this.em.merge(entity));
		}
		catch (RuntimeException e) {
			throw new UOSSException("Fail to delete entity:" + entity.getClass().getName(),e);
		}
	}
}
